package com.xz.message.utils;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.xz.common.constant.Constants;
import com.xz.common.core.redis.RedisCache;
import com.xz.common.exception.ServiceException;
import com.xz.common.utils.spring.SpringUtils;
import com.xz.message.domain.SysTenantNotifyConfig;
import com.xz.message.vo.*;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * @author ZhangZhiJia
 * @date 2023/8/2 15:37
 * @return null
 */
@Component
public class QyapiUtil {

    private static final Logger log = LoggerFactory.getLogger(QyapiUtil.class);

    private RestTemplate restTemplate = new RestTemplate();

    @Autowired
    private QyWxConfig qyWxConfig;
    /**
     * 获取access_token
     */
    public String getToken(SysTenantNotifyConfig tenantConfig) {
       try {
           RedisCache redisCache = SpringUtils.getBean(RedisCache.class);
           String existToken =redisCache.getCacheObject(getCacheKey(tenantConfig.getTenantId()));
           if (StringUtils.isNotBlank(existToken)) {
               return existToken;
           }
           String qyCorpId = tenantConfig.getQyCorpId();
           String qyCorpSecret = tenantConfig.getQyCorpSecret();
           return createToken(qyCorpId, qyCorpSecret, tenantConfig.getTenantId());
       }catch (Exception e){
           log.error(tenantConfig.getTenantId()+"-企业微信获取access_token异常:"+e);
          throw new ServiceException("企业微信获取access_token异常");
       }
    }

    private String createToken(String corpid, String corpsecret, Long tenantId) {
        Map<String, String> param = new HashMap<>();
        param.put("corpid", corpid);
        param.put("corpsecret", corpsecret);
        String res = doGet("https://qyapi.weixin.qq.com/cgi-bin/gettoken", param);
        JSONObject jsonObject = JSONObject.parseObject(res);
        String token = jsonObject.getString("access_token");
        Integer expires = jsonObject.getInteger("expires_in");
        SpringUtils.getBean(RedisCache.class).setCacheObject(getCacheKey(tenantId), token, expires, TimeUnit.SECONDS);
        return token;
    }

    public static String getCacheKey(Long tenantId) {
        return Constants.SYS_QYAPI_KEY + tenantId;
    }


    public String getUserIdbymobile(SysTenantNotifyConfig tenantConfig,String mobile) {
        String token = getToken(tenantConfig);
        Map<String, Object> param = new HashMap<>();
        param.put("mobile", mobile);
        String res = doPost("https://qyapi.weixin.qq.com/cgi-bin/user/getuserid?access_token=" + token , requestBody -> {
            requestBody.putAll(param);
        });
        JSONObject body = JSONObject.parseObject(res);
        if("0".equals(body.getString("errcode"))){
            return body.getString("userid");
        }else{
            throw new ServiceException("根据手机号查找企业微信用户失败,失败原因:"+body.getString("errmsg"));
        }
    }
    /**
     * 根据手机号查询用户
     */
    public QiWeiOrDingDingUser getbymobile(SysTenantNotifyConfig tenantConfig,String mobile) {
        String token = getToken(tenantConfig);
        Map<String, Object> param = new HashMap<>();
        param.put("mobile", mobile);
        String res = doPost("https://qyapi.weixin.qq.com/cgi-bin/user/getuserid?access_token=" + token , requestBody -> {
            requestBody.putAll(param);
        });
        JSONObject body = JSONObject.parseObject(res);
        if("0".equals(body.getString("errcode"))){
            String userid = body.getString("userid");
            return  getbyUserId(userid,token);
        }else{
            throw new ServiceException("根据手机号查找企业微信用户失败,失败原因:"+body.getString("errmsg"));
        }
    }
    public QiWeiOrDingDingUser getbyUserId(String userid,String token) {
        Map<String, String> param = new HashMap<>();
        param.put("access_token", token);
        param.put("userid", userid);
        String res = doGet("https://qyapi.weixin.qq.com/cgi-bin/user/get", param);
        JSONObject body = JSONObject.parseObject(res);
        if("0".equals(body.getString("errcode"))){
            return new Gson().fromJson(res, QiWeiOrDingDingUser.class);
        }else{
            throw new ServiceException("根据手机号查找企业微信用户失败,失败原因:"+body.getString("errmsg"));
        }
    }


    /**
     * 获取部门成员
     */
    public List<DeptUser> getDepartmentMembers(SysTenantNotifyConfig tenantConfig) {
        Map<String, String> param = new HashMap<>();
        param.put("access_token", getToken(tenantConfig));
        param.put("department_id", tenantConfig.getDepartmentId());
        param.put("fetch_child", "1");
        String result = doGet("https://qyapi.weixin.qq.com/cgi-bin/user/simplelist", param);
        Gson gson = new Gson();
        GetDeptUser getDeptUser = gson.fromJson(result, GetDeptUser.class);
        return getDeptUser.getUserlist();
    }

    /**
     * 发送应用消息
     */
    public MessageResponse sendMsg(SysTenantNotifyConfig tenantConfig, MessageBody messageBody) {
        String access_token = getToken(tenantConfig);
        String result = doPost("https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=" + access_token, body -> {
            Map<String, Object> jsonObject = JSONObject.parseObject(JSON.toJSONString(messageBody));
            body.putAll(jsonObject);
        });
        Gson gson = new Gson();
        MessageResponse messageResponse = gson.fromJson(result, MessageResponse.class);
        return messageResponse;
    }
    /**
     * 发送群机器人消息
     */
    public void sendWebhook(String webhook, MessageBody messageBody) {
        doPost(webhook, body -> {
            Map<String, Object> jsonObject = JSONObject.parseObject(JSON.toJSONString(messageBody));
            body.putAll(jsonObject);
        });
    }


    /**
     * 调用post请求
     * 参考博文：https://blog.csdn.net/zai_xia/article/details/80926157
     *
     * @param url      接口地址
     * @param function 封装请求参数
     * @return
     */
    public String doPost(String url, Consumer<Map<String, Object>> function) {
        HttpHeaders headers = new HttpHeaders();
        // 以json的方式提交
        headers.setContentType(MediaType.APPLICATION_JSON);
        Map<String, Object> params = new HashMap<>();
        function.accept(params);
        //将请求头部和参数合成一个请求
        HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(params, headers);
        log.debug("POST访问接口：" + url);
        log.debug("POST body：{}", JSON.toJSONString(params, true));
        //执行HTTP请求，将返回的结构使用ResultVO类格式化
        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
        String body = response.getBody();
        log.debug("接口响应：" + body);
        return body;
    }

    /**
     * 调用get接口
     *
     * @param url           接口地址
     * @param queryParamMap query参数
     * @return
     */
    public String doGet(String url, Map<String, String> queryParamMap) {
        //保证map不为空
        if (!CollectionUtils.isEmpty(queryParamMap)) {
            //遍历map拼接参数
            url += queryParamMap.entrySet().stream()
                    .map(entry -> String.format("%s=%s", entry.getKey(), entry.getValue()))
                    .collect(Collectors.joining("&", "?", ""));
        }
        log.debug("访问接口：" + url);
        //接收响应
        ResponseEntity<String> response = restTemplate.getForEntity(url, String.class);
        log.debug("接口响应：" + response.getBody());
        return response.getBody();
    }

    /**
     * 验证回调URL
     *
     * @param request
     * @return
     */
    public Object verificationUrl(HttpServletRequest request) {
        log.info("=========验证URL有效性开始=========");
        String sEchoStr; //需要返回的明文
        try {
            WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(qyWxConfig.getToken(), qyWxConfig.getEncodingAESKey(),qyWxConfig.getCorpid());
            String msgSignature = request.getParameter("msg_signature");
            String timeStamp = request.getParameter("timestamp");
            String nonce = request.getParameter("nonce");
            String echostr = request.getParameter("echostr");
            log.info("企业微信加密签名: {},时间戳: {},随机数: {},加密的字符串: {}", msgSignature, timeStamp, nonce, echostr);
            sEchoStr = wxcpt.VerifyURL(msgSignature,
                    timeStamp,
                    nonce,
                    echostr);
            log.info("给企业微信返回的明文,{}", sEchoStr);
            log.info("=========验证URL有效性结束=========");
            return sEchoStr;

        } catch (AesException e) {
            log.error("验证URL失败，错误原因请查看异常:{}", e.getCode());
            throw new ServiceException(e.getMessage());
        }
    }

    /**
     * 企业微信回调参数解析
     *
     * @param request
     * @param body
     * @return
     */
    public Map<String, String> getRequestParameter(HttpServletRequest request, String body) {
        log.info("=========参数解析开始=========");
        try {
            WXBizMsgCrypt wxcpt = new WXBizMsgCrypt(qyWxConfig.getToken(), qyWxConfig.getEncodingAESKey(),qyWxConfig.getCorpid() );
            String msgSignature = request.getParameter("msg_signature");
            String timeStamp = request.getParameter("timestamp");
            String nonce = request.getParameter("nonce");
            log.info("企业微信加密签名: {},时间戳: {},随机数: {}", msgSignature, timeStamp, nonce);
            String sMsg = wxcpt.DecryptMsg(msgSignature, timeStamp, nonce, body);
            Map<String, String> resultMap = new HashMap<String, String>(16);
            resultMap = ConstantUtil.parseXmlToMap(sMsg, resultMap);
            log.info("decrypt密文转为map结果为{}", resultMap);
            log.info("=========参数解析结束=========");
            return resultMap;
        } catch (AesException e) {
            log.error("密文参数解析失败，错误原因请查看异常:{}", e.getMessage());
            throw new ServiceException(e.getMessage());
        }
    }
}
